package name.koppernickus.lsp.analyzer.tattletale;

import java.io.*;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

/**
 * Archive scanner
 *
 * @author Jesper Pedersen <jesper.pedersen@jboss.org>
 */
public class ArchiveScanner {

    /**
     * Scan an archive
     *
     * @param file        The file
     * @return The archive
     */
    public static Archive scan(File file) {

        Archive archive = null;
        JarFile jarFile = null;

        try {
            String name = file.getName();
            String filename = file.getCanonicalPath();
            SortedSet<String> requires = new TreeSet<String>();
            SortedMap<String, Long> provides = new TreeMap<String, Long>();
            SortedSet<String> profiles = new TreeSet<String>();
            SortedMap<String, SortedSet<String>> classDependencies = new TreeMap<String, SortedSet<String>>();
            SortedMap<String, SortedSet<String>> packageDependencies = new TreeMap<String, SortedSet<String>>();
            SortedMap<String, SortedSet<String>> blacklistedDependencies = new TreeMap<String, SortedSet<String>>();

            List<String> lSign = null;

            jarFile = new JarFile(file);
            Enumeration<JarEntry> e = jarFile.entries();

            while (e.hasMoreElements()) {
                JarEntry jarEntry = e.nextElement();

                if (jarEntry.getName().endsWith(".class")) {
                    String fullClassName = jarEntry.getName().split("\\.")[0].replaceAll("/", ".");
                    provides.put(fullClassName, 0L);
                }
            }

            if (provides.size() == 0)
                return null;

            String version = null;
            List<String> lManifest = null;
            Manifest manifest = jarFile.getManifest();
            if (manifest != null) {
                Attributes mainAttributes = manifest.getMainAttributes();
                version = mainAttributes.getValue("Specification-Version");
                if (version == null)
                    version = mainAttributes.getValue("Implementation-Version");
                if (version == null)
                    version = mainAttributes.getValue("Version");

                if (version == null && manifest.getEntries() != null) {
                    Iterator ait = manifest.getEntries().values().iterator();
                    while (version == null && ait.hasNext()) {
                        Attributes attributes = (Attributes) ait.next();

                        version = attributes.getValue("Specification-Version");
                        if (version == null)
                            version = attributes.getValue("Implementation-Version");
                        if (version == null)
                            version = attributes.getValue("Version");
                    }
                }

                lManifest = readManifest(manifest);
            }
            Location location = new Location(filename, version);

            archive = new JarArchive(name, 0, lManifest, lSign, requires, provides,
                    classDependencies, packageDependencies, blacklistedDependencies, location);

            if (profiles.size() > 0) {
                for (String profile : profiles) {
                    archive.addProfile(profile);
                }
            }

            Iterator<String> it = provides.keySet().iterator();
            while (it.hasNext()) {
                String provide = it.next();
                requires.remove(provide);
            }
        }
        catch (IOException ioe) {
            // Probably not a JAR archive
        }
        catch (Exception e) {
            System.err.println("Scan: " + e.getMessage());
            e.printStackTrace(System.err);
        }
        finally {
            try {
                if (jarFile != null)
                    jarFile.close();
            }
            catch (IOException ioe) {
                // Ignore
            }
        }

        return archive;
    }

    /**
     * Read the manifest
     *
     * @param manifest The manifest
     * @return The manifest as strings
     */
    private static List<String> readManifest(Manifest manifest) {
        List<String> result = new ArrayList<String>();

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            manifest.write(baos);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            InputStreamReader isr = new InputStreamReader(bais);
            LineNumberReader lnr = new LineNumberReader(isr);

            String s = lnr.readLine();
            while (s != null) {
                result.add(s);
                s = lnr.readLine();
            }
        }
        catch (IOException ioe) {
            // Ignore
        }

        return result;
    }
}
